package org.jiucheng.magpiebridge.client.aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.SocketChannel;

import org.jiucheng.magpiebridge.protocol.Message;
import org.jiucheng.magpiebridge.transfer.aio.TransferAttachment;
import org.jiucheng.magpiebridge.transfer.aio.TransferEstablishmentCompletionHandler;
import org.jiucheng.magpiebridge.util.Cfg;

/**
 * 
 * @author jiucheng
 *
 */
public class ClientReadCompletionHandler implements CompletionHandler<Integer, ClientAttachment> {

	public void completed(Integer result, ClientAttachment attachment) {
        if (result == -1) {
            close(attachment);
            return;
        }
        
        ByteBuffer readByteBuffer = attachment.getReadByteBuffer();
        if (readByteBuffer.position() != readByteBuffer.capacity()) {
            attachment.getClient().read(readByteBuffer, attachment, this);
            return;
        }
        
        if (readByteBuffer.capacity() == 13) {
            // 验证消息头
            readByteBuffer.flip();
            int magic = readByteBuffer.getInt();
            byte type = readByteBuffer.get();
            int uri = readByteBuffer.getInt();
            int size = readByteBuffer.getInt();
            if (magic != Message.MAGIC) {
                close(attachment);
                return;
            }
            if (size > 0) {
                readByteBuffer = ByteBuffer.allocate(size + 13);
                readByteBuffer.putInt(magic);
                readByteBuffer.put(type);
                readByteBuffer.putInt(uri);
                readByteBuffer.putInt(size);
                attachment.getClient().read(readByteBuffer, attachment.setReadByteBuffer(readByteBuffer), attachment.getClientReadCompletionHandler());
                return;
            }
        }
        
        Message message = Message.fromByteBuffer(readByteBuffer);
        byte type = message.getType();
        if (type == Message.Type.CONNECT) {
        	try {
				handleConnectMessage(attachment, message);
			} catch (IOException e) {
				e.printStackTrace();
				attachment.disconnect(message.getUri());
			}
        	readByteBuffer = ByteBuffer.allocate(13);
        	attachment.getClient().read(readByteBuffer, attachment.setReadByteBuffer(readByteBuffer), attachment.getClientReadCompletionHandler());
        } else if (type == Message.Type.HEARTBEAT) {
        	// 心跳不处理
        	readByteBuffer = ByteBuffer.allocate(13);
        	attachment.getClient().read(readByteBuffer, attachment.setReadByteBuffer(readByteBuffer), attachment.getClientReadCompletionHandler());
        }
	}
	
	private void handleConnectMessage(ClientAttachment attachment, Message message) throws IOException {
        int uri = message.getUri();
        // lp.io2c.com
        String ipport = new String(message.getData());
        String[] strs = ipport.split(":");
        
        SocketChannel realSocketChannel = null;
        try {
            realSocketChannel = SocketChannel.open();
            realSocketChannel.connect(new InetSocketAddress(strs[0], Integer.valueOf(strs[1])));
        } catch (NumberFormatException e1) {
            attachment.disconnect(uri);
            return;
        } catch (IOException e1) {
            attachment.disconnect(uri);
            return;
        }
        
        AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
        client.setOption(StandardSocketOptions.SO_REUSEADDR, true);
        client.setOption(StandardSocketOptions.SO_RCVBUF, 8 * 1024 * 1024);
        TransferAttachment transferAttachment = new TransferAttachment().setClient(client).setUri(uri).setRealSocketChannel(realSocketChannel);
        
        transferAttachment.getClient().connect(new InetSocketAddress(Cfg.getServerIp(), Cfg.getServerPort()), transferAttachment.setClientAttachment(attachment), new TransferEstablishmentCompletionHandler());
    }
	
    public void close(ClientAttachment attachment) {
    	attachment.setFailed(true);
    	try {
			attachment.getClient().close();
		} catch (IOException e) {
			e.printStackTrace();
		}
    }

	public void failed(Throwable exc, ClientAttachment attachment) {
		attachment.setFailed(true);
	}
}
